#include "source_call_control.h"
#include "source_debug.h"
#include "source_memory.h"
#include "source_private.h"
#include "source_led_handler.h"
#include "source_usb.h"
#include "source_app_msg_handler.h"
#include "source_aghfp.h"
#include <stdlib.h>

#ifdef DEBUG_CALL_CONTROL
    #define CALL_CONTROL_DEBUG(x) SOURCE_DEBUG(x)

    const char *const call_control_strings[CALL_STATE_TOP] = {
                                                            "CALL_STATE_OFF_HOOK",
                                                            "CALL_STATE_MUTE",
                                                            "CALL_STATE_RING",
                                                            "CALL_STATE_HOLD"
                                                        };
#else
    #define CALL_CONTROL_DEBUG(x)
#endif 

#define CALL_STATE_OFF_HOOK_MASK (1 << CALL_STATE_OFF_HOOK)
#define CALL_STATE_MUTE_MASK     (1 << CALL_STATE_MUTE)
#define CALL_STATE_RING_MASK     (1 << CALL_STATE_RING)
#define CALL_STATE_HOLD_MASK     (1 << CALL_STATE_HOLD)

/* USB data structure */
typedef struct
{
    TaskData callControlTask;
    uint16 spCallState;
    uint16 hsCallState;
} CALL_CONTROL_DATA_T;

CALL_CONTROL_DATA_T CALLCONTROL_RUNDATA;


static void call_control_msg_handler(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(id);
    UNUSED(message);
}


void call_control_init(void)
{
    CALLCONTROL_RUNDATA.callControlTask.handler = call_control_msg_handler;
    CALLCONTROL_RUNDATA.hsCallState = 0;
    CALLCONTROL_RUNDATA.spCallState = 0;
}


static void call_control_state_sync_up(void)
{
    uint16 changes;

    if(states_get_state() != SOURCE_STATE_CONNECTED)
    {
        return;
    }

    changes = CALLCONTROL_RUNDATA.hsCallState ^ CALLCONTROL_RUNDATA.spCallState;

    if(changes & CALL_STATE_OFF_HOOK_MASK)
    {
        if(CALLCONTROL_RUNDATA.spCallState & CALL_STATE_OFF_HOOK_MASK)
        {
            /* off hook is set to ON by softphone */
            if((CALLCONTROL_RUNDATA.hsCallState & CALL_STATE_RING_MASK) || (CALLCONTROL_RUNDATA.spCallState & CALL_STATE_RING_MASK))
            {
                aghfp_call_ind_active();
            }
            else
            {
                aghfp_call_ind_outgoing();
                /* Set to voice mode */
                MessageSend(app_get_instance(), APP_MIC_AUDIO_ACTIVE, 0);
            }

            usb_send_telephony_cmd(USB_HID_TELEPHONY_CMD_HOOK_SWITCH, TRUE);

            CALLCONTROL_RUNDATA.hsCallState |= 1 << CALL_STATE_OFF_HOOK;
        }
        else
        {
            /* off hook is set to OFF by softphone */
            aghfp_call_ind_none();

            usb_send_telephony_cmd(USB_HID_TELEPHONY_CMD_NONE, TRUE);

            CALLCONTROL_RUNDATA.hsCallState &= ~(1 << CALL_STATE_OFF_HOOK);
        }
    }

    if(changes & CALL_STATE_RING_MASK)
    {
        if(CALLCONTROL_RUNDATA.spCallState & CALL_STATE_RING_MASK)
        {
            /* Ring is set to ON by softphone */
            aghfp_call_ind_incoming(0, NULL);

            if(CALLCONTROL_RUNDATA.hsCallState == 0)
            {
                MessageSend(app_get_instance(), APP_MIC_AUDIO_ACTIVE, 0);
            }

            CALLCONTROL_RUNDATA.hsCallState |= 1 << CALL_STATE_RING;
        }
        else
        {
            /* Ring is set to OFF by softphone */
            if(!(CALLCONTROL_RUNDATA.spCallState & CALL_STATE_OFF_HOOK_MASK))
            {
                /* case of call canceled from remote side */
                aghfp_call_ind_none();

                MessageSend(app_get_instance(), APP_MIC_AUDIO_INACTIVE, 0);
            }

            CALLCONTROL_RUNDATA.hsCallState &= ~(1 << CALL_STATE_RING);
        }
    }
}

uint16 call_control_get_sp_state(void)
{
    return CALLCONTROL_RUNDATA.spCallState;
}

void call_control_set_state(uint8 from, CALL_STATE state, bool value)
{
    CALL_CONTROL_DEBUG(("call_control state from %s %s %d\n", from == HEADSET?"headset":"softphone", call_control_strings[state], value));

    if(from == SOFTPHONE)
    {
        switch(state)
        {
        case CALL_STATE_OFF_HOOK:
            if(value)
            {
                CALLCONTROL_RUNDATA.spCallState |= 1 << CALL_STATE_OFF_HOOK;
            }
            else
            {
                CALLCONTROL_RUNDATA.spCallState &= ~(1 << CALL_STATE_OFF_HOOK);
            }

            call_control_state_sync_up();
            break;

        case CALL_STATE_RING:
            if(value)
            {
                CALLCONTROL_RUNDATA.spCallState |= 1 << CALL_STATE_RING;
            }
            else
            {
                CALLCONTROL_RUNDATA.spCallState &= ~(1 << CALL_STATE_RING);
            }

            call_control_state_sync_up();
            break;
        case CALL_STATE_MUTE:

            break;
        default:
            break;
        }
    }
}


void call_control_input(CALL_CONTROL_INPUT input)
{
    switch(input)
    {
    case CALL_CONTROL_INPUT_HOOK_OFF:
        if(CALLCONTROL_RUNDATA.spCallState & CALL_STATE_RING_MASK)
        {
            usb_send_telephony_cmd(USB_HID_TELEPHONY_CMD_HOOK_SWITCH, TRUE);
        }
        break;
    case CALL_CONTROL_INPUT_HOOK_ON:
        if(CALLCONTROL_RUNDATA.hsCallState & CALL_STATE_OFF_HOOK_MASK || CALLCONTROL_RUNDATA.spCallState & CALL_STATE_OFF_HOOK_MASK)
        {
            usb_send_telephony_cmd(USB_HID_TELEPHONY_CMD_HOOK_SWITCH, FALSE);
        }
        break;
    default:
        break;
    }
}


